﻿package com.cat.dao.common.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.util.Assert;

import com.cat.bean.FlowProcessVo;
import com.cat.bean.common.PageBean;
import com.cat.bean.common.Pager;
import com.cat.bean.common.Pager.OrderType;
import com.cat.dao.common.BaseDao;

public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

	private Class<T> entityClass;
	protected SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.entityClass = null;
        Class c = getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClass = (Class<T>) parameterizedType[0];
        }
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public T load(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public List<T> get(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		String hql = "from " + entityClass.getName() + " as model where model.id in(:ids)";
		return getSession().createQuery(hql).setParameterList("ids", ids).list();
	}

	@SuppressWarnings("unchecked")
	public T get(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return (T) getSession().createQuery(hql).setParameter(0, value).uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getList(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return getSession().createQuery(hql).setParameter(0, value).list();
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		String hql = "from " + entityClass.getName();
		return getSession().createQuery(hql).list();
	}
	
	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	public boolean isUnique(String propertyName, Object oldValue, Object newValue) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(newValue, "newValue is required");
		if (newValue == oldValue || newValue.equals(oldValue)) {
			return true;
		}
		if (newValue instanceof String) {
			if (oldValue != null && StringUtils.equalsIgnoreCase((String) oldValue, (String) newValue)) {
				return true;
			}
		}
		T object = get(propertyName, newValue);
		return (object == null);
	}
	
	public boolean isExist(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		T object = get(propertyName, value);
		return (object != null);
	}

	@SuppressWarnings("unchecked")
	public PK save(T entity) {
		Assert.notNull(entity, "entity is required");
		return (PK) getSession().save(entity);
	}

	public void update(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().update(entity);		
	}

	public void delete(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().delete(entity);
	}

	public void delete(PK id) {
		Assert.notNull(id, "id is required");
		T entity = load(id);
		getSession().delete(entity);
	}

	public void delete(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		for (PK id : ids) {
			T entity = load(id);
			getSession().delete(entity);
		}
	}

	public void flush() {
		getSession().flush();
	}
	
	public <E> List<E> findAll(List<E> page, Criteria criteria, final List<Order> orders) {
		return null;
	}

	
	public void clear() {
		getSession().clear();
	}

	public void evict(Object object) {
		Assert.notNull(object, "object is required");
		getSession().evict(object);
	}
	
	public Pager findByPager(Pager pager) {
		if (pager == null) {
			pager = new Pager();
		}
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
		return findByPager(pager, detachedCriteria);
	}

	public Pager findByPager(Pager pager, DetachedCriteria detachedCriteria) {
		if (pager == null) {
			pager = new Pager();
		}
		Integer pageNumber = pager.getPageNumber();
		Integer pageSize = pager.getPageSize();
		String property = pager.getProperty();
		String keyword = pager.getKeyword();
		String orderBy = pager.getOrderBy();
		OrderType orderType = pager.getOrderType();
		
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		if (StringUtils.isNotEmpty(property) && StringUtils.isNotEmpty(keyword)) {
			String propertyString = "";
			if (property.contains(".")) {
				String propertyPrefix = StringUtils.substringBefore(property, ".");
				String propertySuffix = StringUtils.substringAfter(property, ".");
				criteria.createAlias(propertyPrefix, "model");
				propertyString = "model." + propertySuffix;
			} else {
				propertyString = property;
			}
			criteria.add(Restrictions.like(propertyString, "%" + keyword + "%"));
		}
		
		Integer totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
		
		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		criteria.setFirstResult((pageNumber - 1) * pageSize);
		criteria.setMaxResults(pageSize);
		if (StringUtils.isNotEmpty(orderBy) && orderType != null) {
			if (orderType == OrderType.asc) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}
		pager.setTotalCount(totalCount);
		pager.setList(criteria.list());
		return pager;
	}
	
	public int getTotalCount(String hql, Map map) {
		Query query = this.getSession().createQuery(hql);  
			
		if (map != null && map.size() != 0) {
			Iterator it = map.keySet().iterator();  
			while (it.hasNext()) {  
				Object key = it.next();  
				query.setParameter(key.toString(), map.get(key));  
			}  
		}

		Integer i = query.list().size();  

		return i;  
	}  
	
	public int getTotalCount(String sql) {
		Query query = this.getSession().createSQLQuery(sql);  
		Integer i = query.list().size();  

		return i;  
	}  
	
	
	
	/* (non-Javadoc)
	 * @see com.cnooc.dao.common.BaseDao#getCountBySqlDirectly(java.lang.String)
	 */
	public int getCountBySqlDirectly(String sql) {
		return (Integer)getSession().createSQLQuery(sql).addScalar("expectedCount", Hibernate.INTEGER).list().get(0);
	}

	public List<T> findPageByQuery(String hql, Map map, PageBean pageBean) {
		List<Object> result = null;  
        Query query = this.getSession().createQuery(hql);  
        
        if (map != null && map.size() != 0) {
	        Iterator it = map.keySet().iterator();  
			while (it.hasNext()) {
				Object key = it.next();  
				query.setParameter(key.toString(), map.get(key));  
			} 
        }
        
        int currentPage = Integer.parseInt(pageBean.getCurrentPage());
        int pageSize = Integer.parseInt(pageBean.getPageSize());
        
        query.setFirstResult((currentPage - 1) * pageSize);  
        query.setMaxResults(pageSize);  
		return query.list();  
	}
	
	public List<Object[]> findPageByQuery(String sql, PageBean pageBean) {
		List<Object> result = null;  
        Query query = this.getSession().createSQLQuery(sql);  
        
        int currentPage = Integer.parseInt(pageBean.getCurrentPage());
        int pageSize = Integer.parseInt(pageBean.getPageSize());
        
        query.setFirstResult((currentPage - 1) * pageSize);  
        query.setMaxResults(pageSize);  
		return query.list();  
	}
	
	public void updateWorkflowBySql(String hql) {
		getSession().createQuery(hql).executeUpdate();
	}
	
	public void updateBySql(String sql) {
		getSession().createSQLQuery(sql).executeUpdate();
	}

	public List<Object[]> getDraftWorkflowPageBySql(String sql, PageBean pageBean) {
		Query query = getSession().createSQLQuery(sql)
		.addScalar("workflowType", Hibernate.STRING).addScalar("workflowTypeName", Hibernate.STRING)
		.addScalar("workflowId", Hibernate.STRING).addScalar("formTitle", Hibernate.STRING)
		.addScalar("applyDate", Hibernate.STRING).addScalar("applicantName", Hibernate.STRING)
		.addScalar("applicantNotesId", Hibernate.STRING);
        
        int currentPage = Integer.parseInt(pageBean.getCurrentPage());
        int pageSize = Integer.parseInt(pageBean.getPageSize());
        
        query.setFirstResult((currentPage - 1) * pageSize);  
        query.setMaxResults(pageSize);  
		return query.list();  
	}
	
	public List<Object[]> getWorkflowPageBySql(String sql, PageBean pageBean) {
		Query query = getSession().createSQLQuery(sql)
		.addScalar("workflowType", Hibernate.STRING).addScalar("workflowTypeName", Hibernate.STRING)
		.addScalar("workflowId", Hibernate.STRING).addScalar("workflowNum", Hibernate.STRING)
		.addScalar("formTitle", Hibernate.STRING).addScalar("applyDate", Hibernate.STRING)
		.addScalar("launcherName", Hibernate.STRING).addScalar("workflowStatus", Hibernate.STRING).addScalar("workflowstatusname", Hibernate.STRING);
        
		if (pageBean != null) {
	        int currentPage = Integer.parseInt(pageBean.getCurrentPage());
	        int pageSize = Integer.parseInt(pageBean.getPageSize());
	        query.setFirstResult((currentPage - 1) * pageSize);  
	        query.setMaxResults(pageSize);  
		}
		return query.list();  
	}
	
	/**
	 * 根据主键获取流程信息
	 * 
	 * @param workflowId
	 * @return
	 */
	public T getWorkflowById(String workflowId) {
		String hql = "from " + entityClass.getName() + " as model where to_char(model.workflowId) = ? ";
		return (T) getSession().createQuery(hql).setParameter(0, workflowId).uniqueResult();
	}
	
	/**
	 * 根据编码删除流程
	 * 
	 * @param workflowId
	 */
	public void deleteWorkflowRecord(String workflowId) {
		String hql = "Delete " + entityClass.getName() + " where to_char(workflowId) = ?";
		getSession().createQuery(hql).setParameter(0, workflowId).executeUpdate();
	}
	
	/**
	 * 根据流程编码获取流程审批意见
	 * 
	 * @param workflowId
	 * @return
	 */
	public List<T> getWorkflowActionList(String workflowId) {
		String hql = " from " + entityClass.getName() + " where to_char(workflowId) = ? order by operateDate ";
		return getSession().createQuery(hql).setParameter(0, workflowId).list();
	}
	
	/**
	 * 根据流程编码获取流程操作过程
	 * 
	 * @param workflowId
	 * @return
	 */
	public List<T> getWorkflowProcessList(String workflowId) {
		String hql = " from " + entityClass.getName() + " where to_char(workflowId) = ? order by processId ";
		return getSession().createQuery(hql).setParameter(0, workflowId).list();
	}	
	
	/**
	 * 获取操作过程最后结束时间作为新的操作的开始时间
	 * 
	 * @param workflowId
	 * @param workflowType
	 */
	public Date getProcessEndDate(String workflowId, String workflowType) {
		String sql = " select to_char(t.completeddate, 'yyyy-MM-dd HH24:mi:ss') as completeddate from t_" + workflowType + "workflowprocess t, "
			       + " (select max(processid) as processid from t_" + workflowType + "workflowprocess where to_char(workflowid) = '" + workflowId + "' ) mt "
			       + " where t.processid = mt.processid "
			       + " and to_char(t.workflowId) = '" + workflowId + "'";
		Query query = getSession().createSQLQuery(sql).addScalar("completeddate", Hibernate.STRING);
		
		List<Object> dataList = query.list();
		String object = dataList.get(0).toString();
		
		try {
			Date date = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(object));
			return date;
		} catch (ParseException e) {
			return null;
		}
	}
	


	@Override
  public List<FlowProcessVo> getFlowProcessByTaskId(Integer taskId) {
		List<FlowProcessVo> resultList = new ArrayList<FlowProcessVo>();
		StringBuffer sql = new StringBuffer();
		sql.append("SELECT procinst.dbid_ as dbid,actinst.activity_name_ as activityName,");
		sql.append("CONVERT(varchar(16), actinst.start_, 20) as startTime,CONVERT(varchar(16), actinst.end_, 20) as endTime, actinst.htask_ as htask,");
		sql.append("histtask.assignee_ as assignee FROM JBPM4_HIST_ACTINST actinst,");
		sql.append("JBPM4_HIST_PROCINST procinst,JBPM4_HIST_TASK histtask WHERE actinst.hproci_=procinst.dbid_");
		sql.append(" AND histtask.dbid_=actinst.htask_  AND procinst.dbid_=( SELECT task.procinst_");
		sql.append(" FROM JBPM4_TASK task INNER JOIN JBPM4_HIST_TASK hist ON hist.dbid_=task.dbid_");
		sql.append(" WHERE hist.dbid_=").append(taskId).append(") AND actinst.activity_name_!='exclusive1' ORDER BY procinst.dbid_,actinst.dbid_");
		Query query = getSession().createSQLQuery(sql.toString()).addScalar("dbid", Hibernate.INTEGER).addScalar(
		    "activityName", Hibernate.STRING).addScalar("startTime", Hibernate.STRING).addScalar("endTime", Hibernate.STRING)
		    .addScalar("htask", Hibernate.STRING).addScalar("assignee", Hibernate.STRING);

		try {
			List<Object[]> list = query.list();
			if (list != null) {
				Iterator<Object[]> iterator = list.iterator();
				FlowProcessVo process;
				while (iterator.hasNext()) {
					process = new FlowProcessVo();
					Object[] object = iterator.next();
					process.setDbid(Integer.valueOf(String.valueOf(object[0])));
					process.setActivityName(String.valueOf(object[1]));
					process.setStartTime(String.valueOf(object[2]));
					process.setEndTime(String.valueOf(object[3]));
					process.setHtask(Integer.valueOf(String.valueOf(object[4])));
					process.setAssignee(String.valueOf(object[5]));
					resultList.add(process);
				}
			}
		} catch (Exception ex) {
			String e = ex.toString();
		}
		return resultList;
  }
}